/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package Merlion.MRP.bean;

import Merlion.CRM.entity.SalesDocumentEntity;
import Merlion.MRP.entity.BackOrderEntity;
import Merlion.MRP.entity.BillOfMaterialsEntryEntity;
import Merlion.SCM.entity.InventoryBatchEntity;
import Merlion.SCM.entity.ProductEntity;
import java.util.Date;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import Merlion.MRP.entity.PlannedDailyDemandEntity;
import Merlion.MRP.entity.ProductionLineEntity;
import Merlion.MRP.entity.ReservationEntity;
import Merlion.MRP.entity.UnplannedDailyDemandEntity;
import Merlion.SCM.bean.InventoryItemsBeanRemote;
import Merlion.SCM.bean.InventoryMovementsManagementBeanRemote;
import Merlion.SCM.entity.InventoryMovementsRecordEntity;
import Merlion.SCM.entity.MaterialEntity;
import Merlion.util.state.PlannedDemandState;
import Merlion.util.state.ReservationState;
import Merlion.util.state.WarehouseState;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import javax.ejb.EJB;
import javax.persistence.Query;
import javax.persistence.TemporalType;

@Stateless
public class ProductionManagementBean implements ProductionManagementBeanRemote {

    @PersistenceContext
    EntityManager em;
    //String //message;
    //StatusMessage messages = new StatusMessage();
    //DateOperator date = new DateOperator();
    //DemandManagementBean dmb = new DemandManagementBean();
    @EJB
    private DemandManagementBeanRemote dmb;
    @EJB
    private InventoryMovementsManagementBeanRemote imm;
    @EJB
    private InventoryItemsBeanRemote iib;
    /*
    private int getProductionFloor1RemainingCapacity(ProductionLine1Entity pf, ProductEntity product, boolean overtime) {
    
    double remainingNormalShift = pf.getRemainingNormalShift();
    double remainingOvertimeShift = pf.getRemainingOvertimeShift();
    int capacity = 0; //in cases
    
    if (pf.getLastProductInLine() != product.getId()) { // changeover penalty
    remainingNormalShift -= pf.getChangeOverTime();
    }
    
    if (remainingNormalShift > 0) {
    capacity += remainingNormalShift * product.getProductionRate();
    }
    
    if (overtime && remainingOvertimeShift > 0) {
    capacity += remainingOvertimeShift * product.getProductionRate();
    }
    
    return capacity;
    }
    
    private boolean createProductBatchAtProductionFloor1(ProductionLine1Entity pf, ProductEntity product, int quantity, boolean overtime) {
    
    double remainingNormalShift = pf.getRemainingNormalShift();
    double remainingOvertimeShift = pf.getRemainingOvertimeShift();
    int remaining = quantity; //in cases
    
    if (pf.getLastProductInLine() != product.getId()) { // changeover penalty
    remainingNormalShift -= pf.getChangeOverTime();
    }
    
    if (remainingNormalShift > 0) {
    int temp = (int) Math.min(remaining, remainingNormalShift * product.getProductionRate());
    int diff = remaining - temp;
    remaining -= temp;
    pf.setRemainingNormalShift(diff / product.getProductionRate());
    }
    
    if (remaining <= 0) {
    pf.setLastProductInLine(product.getId());
    em.flush();
    return true;
    }
    
    if (overtime && remainingOvertimeShift > 0) {
    int temp = (int) Math.min(remaining, remainingOvertimeShift * product.getProductionRate());
    int diff = remaining - temp;
    remaining -= temp;
    pf.setRemainingOvertimeShift(diff / product.getProductionRate());
    }
    
    if (remaining <= 0) {
    pf.setLastProductInLine(product.getId());
    em.flush();
    return true;
    }
    
    return false;
    }
     */

    /*
    private boolean createProductBatchAtProductionLine(ProductionLineEntity pf, ProductEntity product, int quantity, boolean overtime) {
    
    double remainingNormalShift = pf.getRemainingNormalShift();
    double remainingOvertimeShift = pf.getRemainingOvertimeShift();
    int remaining = quantity; //in cases
    
    if (pf.getLastProductInLine() != product.getId()) { // changeover penalty
    remainingNormalShift -= pf.getChangeOverTime();
    }
    
    if (remainingNormalShift > 0) {
    int temp = (int) Math.min(remaining, remainingNormalShift * product.getProductionRate());
    int diff = remaining - temp;
    remaining -= temp;
    pf.setRemainingNormalShift(diff / product.getProductionRate());
    }
    
    if (remaining <= 0) {
    pf.setLastProductInLine(product.getId());
    em.flush();
    return true;
    }
    
    if (overtime && remainingOvertimeShift > 0) {
    int temp = (int) Math.min(remaining, remainingOvertimeShift * product.getProductionRate());
    int diff = remaining - temp;
    remaining -= temp;
    pf.setRemainingOvertimeShift(diff / product.getProductionRate());
    }
    
    if (remaining <= 0) {
    pf.setLastProductInLine(product.getId());
    em.flush();
    return true;
    }
    
    return false;
    }
    /*
    private int getProductionFloor2RemainingCapacity(ProductionLine2Entity pf, ProductEntity product, boolean overtime) {
    
    double remainingNormalShift = pf.getRemainingNormalShift();
    double remainingOvertimeShift = pf.getRemainingOvertimeShift();
    int capacity = 0; //in cases
    
    if (pf.getLastProductInLine() != product.getId()) { // changeover penalty
    remainingNormalShift -= pf.getChangeOverTime();
    }
    
    if (remainingNormalShift > 0) {
    capacity += remainingNormalShift * product.getProductionRate();
    }
    
    if (overtime && remainingOvertimeShift > 0) {
    capacity += remainingOvertimeShift * product.getProductionRate();
    }
    
    return capacity;
    }
     */
    private int getProductionFloorRemainingCapacity(ProductionLineEntity pf, ProductEntity product, boolean overtime) {

        double remainingNormalShift = pf.getRemainingNormalShift();
        double remainingOvertimeShift = pf.getRemainingOvertimeShift();
        int capacity = 0; //in cases

        if (pf.getLastProductInLine() != product.getId()) { // changeover penalty
            remainingNormalShift -= pf.getChangeOverTime();
        }

        if (remainingNormalShift > 0) {
            capacity += remainingNormalShift * product.getProductionRate();
        }

        if (overtime && remainingOvertimeShift > 0) {
            capacity += remainingOvertimeShift * product.getProductionRate();
        }

        return capacity;
    }
    /*
    private boolean createProductBatchAtProductionFloor2(ProductionLine2Entity pf, ProductEntity product, int quantity, boolean overtime) {
    
    double remainingNormalShift = pf.getRemainingNormalShift();
    double remainingOvertimeShift = pf.getRemainingOvertimeShift();
    int remaining = quantity; //in cases
    
    if (pf.getLastProductInLine() != product.getId()) { // changeover penalty
    remainingNormalShift -= pf.getChangeOverTime();
    }
    
    if (remainingNormalShift > 0) {
    int temp = (int) Math.min(remaining, remainingNormalShift * product.getProductionRate());
    int diff = remaining - temp;
    remaining -= temp;
    pf.setRemainingNormalShift(diff / product.getProductionRate());
    }
    
    if (remaining <= 0) {
    pf.setLastProductInLine(product.getId());
    em.flush();
    return true;
    }
    
    if (overtime && remainingOvertimeShift > 0) {
    int temp = (int) Math.min(remaining, remainingOvertimeShift * product.getProductionRate());
    int diff = remaining - temp;
    remaining -= temp;
    pf.setRemainingOvertimeShift(diff / product.getProductionRate());
    }
    
    if (remaining <= 0) {
    pf.setLastProductInLine(product.getId());
    em.flush();
    return true;
    }
    
    return false;
    }
    
    protected ProductionFloorEntity createDailyProductionFloor(Date mfgDate) {
    
    ProductionFloorEntity pf = new ProductionFloorEntity(mfgDate);
    
    return pf;
    
    }
     */

    /*
    @Override
    public boolean createProductBatch(Long productId, Integer yearInt, Integer monthInt, Integer weekOfMonth, Integer dayOfMonth, boolean overtime, int productionLine) {
    
    int quantity;
    
    System.out.println("Creating product batch for product Id: " + productId.toString());
    PlannedDailyDemandEntity pdd = dmb.getPlannedDailyDemand(productId, yearInt, monthInt, weekOfMonth, dayOfMonth);
    if (pdd == null) {
    
    System.out.println("Cannot find corresponding planned daily demand");
    return false;
    }
    System.out.println(pdd.toString() + " successfully fetched ");
    
    ProductEntity product = pdd.getProduct();
    quantity = pdd.getDailyDemand();
    
    Date mfgDate = pdd.getExecDate();
    
    Query q = em.createQuery("SELECT pf FROM ProductionFloorEntity pf WHERE pf.mfgDate = :mfgDate").setParameter("mfgDate", mfgDate, TemporalType.DATE);
    List<ProductionLineEntity> lines = new ArrayList();
    ProductionFloorEntity pf;
    
    try {
    pf = (ProductionFloorEntity) q.getSingleResult();
    } catch (NoResultException e) {
    ProductionLineEntity line1 = new ProductionLineEntity(mfgDate, 1);
    ProductionLineEntity line2 = new ProductionLineEntity(mfgDate, 2);
    pf = new ProductionFloorEntity(mfgDate, line1, line2);
    em.persist(line1);
    em.persist(line2);
    em.persist(pf);
    }
    
    lines = (List<ProductionLineEntity>) pf.getProductionLines();
    
    ProductionLineEntity line;
    
    switch (productionLine) {
    case 1:
    line = lines.get(0);
    break;
    case 2:
    line = lines.get(1);
    break;
    default:
    System.out.println("Cannot create product batch. Production line not found.");
    return false;
    }
    
    int shelfLife = product.getShelfLife();
    Date expDate = DateUtils.addWeeks(mfgDate, shelfLife);
    
    int capacity = 0;
    //check production floor remaining capacity
    
    capacity = getProductionFloorRemainingCapacity(line, product, overtime);
    
    if (quantity > capacity) {
    System.out.println("Cannot create product batch. Line capacity exceeded.");
    return false;
    }
    if (!this.createProductBatchAtProductionLine(line, product, quantity, overtime)) {
    System.out.println("Cannot create product batch. Unexpected Exception.");
    return false;
    }
    
    InventoryBatchEntity batch = new InventoryBatchEntity(product, mfgDate, expDate, quantity);
    em.persist(batch);
    
    pdd.setProductBatch(batch);
    batch.setPlannedDailyDemand(pdd);
    batch.setPlannedDailyDemand(pdd);
    product.setOrderedQty(product.getOrderedQty() + quantity);
    em.flush();
    if (quantity > pdd.getDailyDemand()) {
    int increment = quantity - pdd.getDailyDemand();
    UnplannedDailyDemandEntity udd = new UnplannedDailyDemandEntity(pdd, increment);
    em.persist(udd);
    udd.setPlannedDailyDemandEntity(pdd);
    List<UnplannedDailyDemandEntity> udds = new ArrayList<UnplannedDailyDemandEntity>();
    udds = (List<UnplannedDailyDemandEntity>) pdd.getUnplannedDailyDemandEntitys();
    if (!udds.add(udd)) {
    throw new RuntimeException("createProductBatch: Unexpected Exception");
    }
    pdd.setUnplannedDailyDemandEntitys(udds);
    em.flush();
    }
    
    return true;
    
    }
     */
    /*
    public void createProductBatch(Long productId, Date mfgDate, Date expiryDate, int qty) {
    //manually input the manufacturing and expiry dates
    ProductEntity product = em.find(ProductEntity.class, productId);
    if (product != null) {
    InventoryBatchEntity batch = new InventoryBatchEntity(product, mfgDate, expiryDate, qty);
    em.persist(batch);
    product.setInHandInventory(product.getInHandInventory() + qty);
    em.flush();
    //message = "Product Batch successfully created.";
    //messages.addMessage(//message);
    } else {
    //message = "Product Batch creation failed. Check Product ID.";
    //messages.addMessage(//message);
    }
    }
    
    public void createProductBatch(Long productId, int qty) {
    //automatically input the manufacturing and expiry dates
    ProductEntity product = em.find(ProductEntity.class, productId);
    Date mfgDate = new Date();
    Date expiryDate = date.addMonths(mfgDate, product.getShelfLife());
    if (product != null) {
    InventoryBatchEntity batch = new InventoryBatchEntity(product, mfgDate, expiryDate, qty);
    em.persist(batch);
    product.setInHandInventory(product.getInHandInventory() + qty);
    em.flush();
    //message = "Product Batch successfully created.";
    //messages.addMessage(//message);
    } else {
    //message = "Product Batch creation failed. Check Product ID.";
    //messages.addMessage(//message);
    }
    }
     */

    /*
    @Override
    public void withdrawProductBatch(Long batchId, String remarks) {
    //logical deletion only
    InventoryBatchEntity batch = em.find(InventoryBatchEntity.class, batchId);
    if (batch != null) {
    batch.setDelFlg("Y");
    batch.setRemarks(remarks);
    ProductEntity product = batch.getProduct();
    int newQty = product.getInHandQty() - batch.getCommittedQty();
    product.setInHandQty(newQty);
    em.flush();
    //message = "Product Batch successfully withdrawn.";
    //messages.addMessage(//message);
    } else {
    //message = "Product Batch withdrawal failed. Check Product ID.";
    //messages.addMessage(//message);
    }
    }
    
    
     */
    @Override
    public void initializeProductBatches() {
        System.out.println("Initializing product batches...");
        Calendar cal = Calendar.getInstance();
        cal.add(Calendar.DAY_OF_MONTH, -1); // product everything from last month up to today
        Date toDate = cal.getTime();
        cal.add(Calendar.MONTH, -1); //initialize all product batches from month - 1
        Date fromDate = cal.getTime();
        dmb.commitMonthDailyPlannedDemand(2011, 10);
        dmb.commitMonthDailyPlannedDemand(2011, 11);
        System.out.println("SELECT a FROM PlannedDailyDemandEntity a where a.execDate BETWEEN " + fromDate.toString() + " AND " + toDate.toString());
        Query q = em.createQuery("SELECT a FROM PlannedDailyDemandEntity a where a.execDate BETWEEN :fromDate AND :toDate");
        q.setParameter("toDate", toDate, TemporalType.DATE);
        q.setParameter("fromDate", fromDate, TemporalType.DATE);

        List<WarehouseState> warehouses = iib.viewWarehouse();
        Long warehouseId = warehouses.get(0).getId();
        for (Object o : q.getResultList()) {
            PlannedDailyDemandEntity pdd = (PlannedDailyDemandEntity) o;
            System.out.println("Committing and creating " + pdd.toString());
            Long batchId = this.createProductBatch(pdd.getId(), "INIT");
            imm.addInventoryBatchToWarehouse(batchId, warehouseId);
        }
    }

    @Override
    public void initializeMaterialBatches() {
        System.out.println("Initializing Material Matches");
        Calendar cal = Calendar.getInstance();
        Date date = cal.getTime();
        imm.executeWeeklyIndividualRequirement(2011, 9, 1, date, "INIT");
        imm.executeWeeklyIndividualRequirement(2011, 9, 2, date, "INIT");
        imm.executeWeeklyIndividualRequirement(2011, 9, 3, date, "INIT");
        imm.executeWeeklyIndividualRequirement(2011, 9, 4, date, "INIT");
        imm.executeWeeklyIndividualRequirement(2011, 9, 5, date, "INIT");
        imm.executeWeeklyIndividualRequirement(2011, 9, 6, date, "INIT");
        imm.executeWeeklyIndividualRequirement(2011, 10, 1, date, "INIT");
        imm.executeWeeklyIndividualRequirement(2011, 10, 2, date, "INIT");
        imm.executeWeeklyIndividualRequirement(2011, 10, 3, date, "INIT");
        imm.executeWeeklyIndividualRequirement(2011, 10, 4, date, "INIT");
        imm.executeWeeklyIndividualRequirement(2011, 10, 5, date, "INIT");
        imm.executeWeeklyIndividualRequirement(2011, 10, 6, date, "INIT");
        imm.executeWeeklyIndividualRequirement(2011, 11, 1, date, "INIT");
        imm.executeWeeklyIndividualRequirement(2011, 11, 2, date, "INIT");

        List<WarehouseState> warehouses = iib.viewWarehouse();
        Long warehouseId = warehouses.get(0).getId();

        Query q = em.createQuery("SELECT a FROM InventoryBatchEntity a WHERE a.warehouse IS NULL");
        for (Object o : q.getResultList()) {
            System.out.println("Adding material to warehouse");
            InventoryBatchEntity batch = (InventoryBatchEntity) o;
            imm.addInventoryBatchToWarehouse(batch.getId(), warehouseId);
        }

    }

    @Override
    public Long createProductBatch(String plannedDailyDemandId, String remarks) {
        System.out.println("Creating product batch..");
        PlannedDailyDemandEntity pdd = em.find(PlannedDailyDemandEntity.class, plannedDailyDemandId);

        if (pdd.isCreated()) {
            return pdd.getProductBatch().getId();
        }

        ProductEntity product = pdd.getProduct();
        int productionQty = pdd.getAdjustedQty();
        Date mfgDate = pdd.getExecDate();
        int reservedQty = pdd.getReservedQty();
        int inHandQty = pdd.getAdjustedQty() - reservedQty;

        //updating product
        product.setOrderedQty(product.getOrderedQty() - productionQty);
        System.out.println("Updating product..");

        //updating materials
        List<BillOfMaterialsEntryEntity> entries = new ArrayList();
        entries = (List<BillOfMaterialsEntryEntity>) product.getBOMEntity().getEntries();
        double qtyInKg = productionQty * product.getCaseWeight();
        double multiplier = qtyInKg / 200; // BOM multiplier, products are created in 200kg batches

        for (BillOfMaterialsEntryEntity entry : entries) {
            int requiredQty = (int) (multiplier * entry.getQuantity() + 0.5); // quantity required (unit material)
            MaterialEntity material = entry.getIngredient();
            int inHandQtyRaw = material.getInHandQty() - requiredQty;
            InventoryMovementsRecordEntity log = new InventoryMovementsRecordEntity();
            //assume a single pool for material as material purchasing module is not implemented
            log.createLog(material.getId(), material.getName(), requiredQty, "OUTBOUND", new Long(99), "Material Pool");
            if (inHandQtyRaw < 0) {
                throw new IllegalStateException("Insufficient material..");
            }
            em.persist(log);
            em.flush();
            material.setInHandQty(inHandQtyRaw);
        }
        System.out.println("Updating materials");

        Calendar cal = Calendar.getInstance();
        cal.setTime(mfgDate);
        cal.add(Calendar.MONTH, product.getShelfLife());
        Date expiryDate = cal.getTime();
        InventoryBatchEntity batch = new InventoryBatchEntity();
        batch.createProductBatch(product, expiryDate, productionQty, inHandQty, reservedQty, remarks, mfgDate, pdd);
        em.persist(batch);
        InventoryMovementsRecordEntity log = new InventoryMovementsRecordEntity();
        pdd.setCreated(true);
        em.flush();
        return batch.getId();
    }

    @Override
    public void withdrawProductBatch(Long batchId, String remarks) {
        System.out.println("Withdrawing Product Batch");
        InventoryBatchEntity batch = em.find(InventoryBatchEntity.class, batchId);
        imm.discardMaterialBatchFromWarehouse(batchId, batch.getWarehouse().getId(), remarks);

    }

    @Override
    public BackOrderEntity createBackOrder(Long salesDocId) {
        SalesDocumentEntity sd = em.find(SalesDocumentEntity.class, salesDocId);

        if (sd == null) {
            throw new NullPointerException();
        }

        BackOrderEntity bo = new BackOrderEntity();

        return bo;
    }

    @Override
    public void withdrawBackOrder(Long backOrderId) {
        // to be integrated with SalesOrderProcessing
        BackOrderEntity bo = em.find(BackOrderEntity.class, backOrderId);

        if (bo == null) {
            throw new NullPointerException();
        }

        SalesDocumentEntity sd = bo.getSalesDocument();
        List<ReservationEntity> reservations = new ArrayList();
        reservations = (List<ReservationEntity>) bo.getReservationEntities();

        for (ReservationEntity reservation : reservations) {
            this.withdrawReservation(reservation.getId());
        }

        sd.setBackOrder(null);
        bo.setSalesDocument(null);
        bo.setVoided(true);

        em.flush();

    }
    /*
    @Override
    public ReservationEntity createReservation(BackOrderEntity bo, PlannedDailyDemandEntity pdd, int quantity) {
    
    ReservationEntity reservation = new ReservationEntity();
    
    reservation.setBackOrderEntity(bo);
    reservation.setPlannedDailyDemandEntity(pdd);
    reservation.setQuantity(quantity);
    
    pdd.setReservedQty(pdd.getReservedQty() + quantity);
    
    em.persist(reservation);
    em.flush();
    
    return reservation;
    
    }
     */

    @Override
    public void confirmBackOrder(Long backOrderId) {
        //to be integrated with SalesOrderProcessing (confirmReservationsOnSalesOrder)
        BackOrderEntity bo = em.find(BackOrderEntity.class, backOrderId);

        if (bo == null) {
            throw new NullPointerException();
        }

        List<ReservationEntity> reservations = new ArrayList();
        reservations = (List<ReservationEntity>) bo.getReservationEntities();

        // Need to commit the unplanned demand raised previously, reservations made on inventory
        // and planned demand are not altered
        for (ReservationEntity reservation : reservations) {
            UnplannedDailyDemandEntity upp = reservation.getUnplannedDailyDemand();

            if (upp != null) {
                dmb.commitUnplannedDailyDemand(upp.getId());
            }
        }

        em.flush();

    }

    @Override
    public void withdrawReservation(Long id) {
        //to be integrated with DemandManagement (calculateLeadTime)
        ReservationEntity reservation = em.find(ReservationEntity.class, id);
        if (reservation != null) {
            System.out.println("Reservation not null");
        }
        int reservedQty = reservation.getQuantity();

        if (reservation == null) {
            throw new NullPointerException();
        }

        PlannedDailyDemandEntity pdd = reservation.getPlannedDailyDemandEntity();
        InventoryBatchEntity batch = reservation.getInventoryBatch();
        UnplannedDailyDemandEntity udd = reservation.getUnplannedDailyDemand();

        if (pdd != null && !pdd.isCreated()) {
            pdd.setReservedQty(pdd.getReservedQty() - reservedQty);
            pdd.setDepleted(false);
            List<ReservationEntity> reservations = new ArrayList();
            reservations = (List<ReservationEntity>) pdd.getReservationEntities();
            reservations.remove(reservation);
            pdd.setReservationEntities(reservations);
            System.out.println("Voiding reservation on planned demand" + pdd.toString());
            reservation.setPlannedDailyDemandEntity(null);
            reservation.setVoided(true);
            em.flush();
        }

        if (batch != null) {
            batch.setReservedQty(batch.getReservedQty() - reservedQty);
            batch.setInHandQty(batch.getInHandQty() + reservedQty);
            List<ReservationEntity> reservations = new ArrayList();
            reservations = (List<ReservationEntity>) batch.getReservationEntities();
            reservations.remove(reservation);
            batch.setReservationEntities(reservations);

            ProductEntity product = batch.getProduct();

            product.setInHandQty(product.getInHandQty() + reservedQty);
            product.setReservedQty(product.getReservedQty() - reservedQty);
            System.out.println("Voiding reservation on planned demand" + batch.toString());
            reservation.setInventoryBatch(null);
            reservation.setVoided(true);
            em.flush();
        }

        if (udd != null) {
            dmb.deleteUnplannedDailyDemand(udd.getId());
            System.out.println("Voiding reservation on unplanned demand" + udd.toString());
        }
    }

    @Override
    public List<ReservationState> viewReservationsOnSalesDocument(Long salesDocId) {

        SalesDocumentEntity sd = em.find(SalesDocumentEntity.class, salesDocId);

        /*  if (!sd.getDocumentType().equals("QN") || !sd.getDocumentType().equals("SO")) {
        throw new IllegalArgumentException("The id provided does not belong to a valid quotation or sales order");
        }*/
        SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
        BackOrderEntity bo = sd.getBackOrder();

        List<ReservationEntity> reservations = bo.getReservationEntities();
        List<ReservationState> states = new ArrayList();
        for (ReservationEntity reservation : reservations) {
            InventoryBatchEntity batch = reservation.getInventoryBatch();
            PlannedDailyDemandEntity pdd = reservation.getPlannedDailyDemandEntity();
            UnplannedDailyDemandEntity udd = reservation.getUnplannedDailyDemand();
            ReservationState state = new ReservationState();
            if (batch != null) {
                state.createReservation_INV(reservation.getId(), reservation.getProductId(), reservation.getQuantity(), reservation.getStatus(), reservation.isVoided(), reservation.isPicked(), batch.getId(), batch.getWarehouse().getId());
                state.setAvailabilityDate("IMMEDIATE");
            }

            if (pdd != null) {
                state.createReservation_PDD(reservation.getId(), reservation.getProductId(), reservation.getQuantity(), reservation.getStatus(), reservation.isVoided(), reservation.isPicked(), pdd.getId());
                state.setAvailabilityDate(sdf.format(pdd.getExecDate()));
            }

            if (udd != null) {
                state.createReservation_BKO(reservation.getId(), reservation.getProductId(), reservation.getQuantity(), reservation.getStatus(), reservation.isVoided(), reservation.isPicked(), udd.getId());
                state.setAvailabilityDate(sdf.format(udd.getExecutionDate()));
            }

            states.add(state);
        }

        return states;

    }

    @Override
    public List<PlannedDemandState> viewTodayProductionPlan() {
        Calendar cal = Calendar.getInstance();
        Date execDate = cal.getTime();

        Query q = em.createQuery("SELECT a FROM PlannedDailyDemandEntity a WHERE a.execDate = :execDate AND a.created = :created AND a.committed = :committed");
        q.setParameter("execDate", execDate, TemporalType.DATE);
        q.setParameter("created", false);
        q.setParameter("committed", true);

        List<PlannedDemandState> states = new ArrayList();
        for (Object o : q.getResultList()) {
            PlannedDailyDemandEntity pdd = (PlannedDailyDemandEntity) o;

            PlannedDemandState state = new PlannedDemandState();
            state.createDailyPlannedDemandState(pdd.getId(), pdd.getYearInt(), pdd.getMonthInt(), pdd.getWeekOfMonth(), pdd.getDayOfMonth(), pdd.getProduct().getId(), pdd.getPlannedWeeklyDemand().getPlannedMonthlyDemand().getMonthlyDemand(), pdd.getDailyDemand(), pdd.getPlannedWeeklyDemand().getPlannedMonthlyDemand().getnWorkdays());
            state.setAdjustedQty(pdd.getAdjustedQty());
            state.setProductName(pdd.getProduct().getName());

            states.add(state);
        }

        return states;
    }

    public boolean executeTodayProductionPlan() {
        List<PlannedDemandState> states = this.viewTodayProductionPlan();
        Calendar cal = Calendar.getInstance();
        Date execDate = cal.getTime();

        for (PlannedDemandState state : states) {
            try {
            this.createProductBatch(state.getId(), "Executed successfully on " + execDate);
            } catch (Exception e) {
                System.out.println (e.getMessage() + " for " + state.getId());
            }
        }

        return true;
    }
}
